﻿/*=========================================================================================================================/
 * 
 * FILE: StateManager.cs
 * AUTH: Alex Landovskis
 * DATE: 10/30/2011
 * CHANGE: 11/03/2011
 * 
 * ADDED:
 *          - Fixed menu fall-through bug
 * 
 *========================================================================================================================*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace Homebound
{
    #region StateNode
    public class StateNode
    {
        public List<StateNode> previous;
        public State state;
        public List<string> choices;
        public List<StateNode> next;

        public StateNode(List<string> newChoices, State newState)
        {
            state = newState;
            choices = newChoices;
            previous = new List<StateNode>();
            next = new List<StateNode>();
        }

        // Check if there is a previous state. 
        public bool hasPrevious()
        {
            return (previous.Count() > 0);
        }

        // Check if there is a next state. 
        public bool hasNext()
        {
            return (next.Count() > 0);
        }
    }
    #endregion StateNode

    public class StateManager
    {
        private GraphicsDeviceManager graphics;
        StateNode currentStateNode;
        public List<Keys> keys;
        public State currentState
        {
            get
            {
                return currentStateNode.state;
            }
        }

        // Bool enter can be pressed safely
        bool nextEnabled = true;

        public StateManager(GraphicsDeviceManager newGraphics, SpriteFont newFont, SpriteFont newTitleFont, SpriteFont newHudFont, GameServiceContainer newServices, ContentManager newContent) 
        {
            graphics = newGraphics;
            
            // Create state instances.
            TitleState titleState = new TitleState(newFont, newTitleFont, newGraphics, newServices, newContent);
            LevelSelectState levelSelectState = new LevelSelectState(newFont, newTitleFont, newGraphics, newServices, newContent);
            OptionsState optionsState = new OptionsState(newFont, newTitleFont, newGraphics, newServices, newContent);
            ControlsState controlsState = new ControlsState(newFont, newTitleFont, newGraphics, newServices, newContent);
            GamePlayState gamePlayState = new GamePlayState(newFont, newTitleFont, newHudFont, newGraphics, newServices, newContent);
            GameOverState gameOverState = new GameOverState(newGraphics, newServices, newContent);
            WinState winState = new WinState(newGraphics, newServices, newContent);
            PauseState pauseState = new PauseState(newGraphics, newServices, newContent);

            // Link the states together
            StateNode titleNode = new StateNode(titleState.choices, titleState);
            StateNode levelSelectNode = new StateNode(new List<string>(), levelSelectState);
            StateNode optionsNode = new StateNode(optionsState.choices, optionsState);
            StateNode controlsNode = new StateNode(new List<string>(), controlsState);
            StateNode gamePlayNode = new StateNode(new List<string>(), gamePlayState);
            StateNode gameOverNode = new StateNode(new List<string>(), gameOverState);
            StateNode winNode = new StateNode(new List<string>(), winState);
            StateNode pauseNode = new StateNode(new List<string>(), pauseState);

            titleNode.next.Add(levelSelectNode);
            levelSelectNode.previous.Add(titleNode);
            levelSelectNode.next.Add(gamePlayNode);

            gamePlayNode.previous.Add(levelSelectNode);
            gamePlayNode.next.Add(winNode);
            gameOverNode.next.Add(titleNode);
            gamePlayNode.next.Add(gameOverNode);
            gamePlayNode.next.Add(pauseNode);
            pauseNode.previous.Add(gamePlayNode);
            
            State prev = null;
            currentStateNode = titleNode;
            currentStateNode.state.onEnter(prev);
        }

        public State current()
        {
            return currentState;
        }

        public bool previous()
        {
            // Does the current state have a previous state?
            if (currentStateNode.hasPrevious())
            {
                // Are the conditions to go back to the previous state satisified?
                if (currentStateNode.previous[0].state.goingBack(currentState))
                {
                    currentStateNode.state.onExit(currentStateNode.previous[0].state);
                    currentStateNode = currentStateNode.previous[0];
                    currentStateNode.state.onEnter(currentStateNode.state);
                    return true;
                }
            }
            return false;
        }

        // Goes to the next state.
        public bool next()
        {
            int i;

            // Disable going to next state (prevent fall-through)
            //nextEnabled = false;

            // Does the current state have a next state?
            if (currentStateNode.hasNext())
            {
                StateNode prevNode = currentStateNode;
                // Are the conditions to go to the next state satisified?
                if (currentStateNode.choices.Count == 0)
                {
                    foreach (StateNode nextStateNode in currentStateNode.next)
                    {
                        if (nextStateNode.state.goingForward(currentState))
                        {
                            currentStateNode.state.onExit(nextStateNode.state);
                            currentStateNode = nextStateNode;
                            currentStateNode.state.onEnter(prevNode.state);
                            return true;
                        }
                    }
                }
                else
                {
                    // Determine if the state is a user selection state.
                    for (i = 0; i < currentStateNode.next.Count(); i++)
                    {
                        // Check if the desired state has been found.
                        if (currentStateNode.choices[i] == currentState.choices[currentState.selectedChoice] && currentStateNode.next[i].state.goingForward(currentState))
                        {
                            currentStateNode.state.onExit(currentStateNode.next[i].state);
                            currentStateNode = currentStateNode.next[i];
                            currentStateNode.state.onEnter(prevNode.state);
                            return true;
                        }
                    }
                }                
            }
            return false;
        }

        // Handles the input that is common to all states.
        public void input (GameTime gameTime)
        {
            keys = new List<Keys>(Keyboard.GetState().GetPressedKeys());
            GamePadButtons buttons = GamePad.GetState(PlayerIndex.One).Buttons;
            GamePadDPad dpad = GamePad.GetState(PlayerIndex.One).DPad;
            GamePadThumbSticks thumbsticks = GamePad.GetState(PlayerIndex.One).ThumbSticks;

            // Allow each state to override default actions.
            currentStateNode.state.input(gameTime, keys, buttons, dpad, thumbsticks);

            if ((Keyboard.GetState().IsKeyDown(Keys.Enter) || GamePad.GetState(PlayerIndex.One).IsButtonDown(Buttons.A)) && nextEnabled)
            {
                nextEnabled = false;
                currentStateNode.state.enterPressed = true;
                next();
            }
            else if ((Keyboard.GetState().IsKeyDown(Keys.Escape) || GamePad.GetState(PlayerIndex.One).IsButtonDown(Buttons.Back)) && nextEnabled) // The back or Escape key was pressed.
            {
                nextEnabled = false;
                previous();
            }

            // Enable going to the next state, only if selection button is no longer pressed
            if ((GamePad.GetState(PlayerIndex.One).IsButtonUp(Buttons.A) && Keyboard.GetState().IsKeyUp(Keys.Enter)) && 
                (GamePad.GetState(PlayerIndex.One).IsButtonUp(Buttons.Back) && Keyboard.GetState().IsKeyUp(Keys.Escape)))
                nextEnabled = true;
        }

        // Handles updates common to all states.
        public void update(GameTime gameTime)
        {
            previous();
            currentState.update(gameTime);
            next();
        }

        // Handles drawing that is common to all states.
        public void draw(SpriteBatch spriteBatch, GameTime gameTime)
        {
            Vector2 screenSize = new Vector2(graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight);

            currentState.draw(spriteBatch, gameTime, screenSize);
        }
    }
}
